package com.wxlsoft.config.deploy;

import java.time.LocalDateTime;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

import com.wxlsoft.config.model.ConfigFileResource;
import com.wxlsoft.config.model.EsbAppInfo;
import com.wxlsoft.config.model.EsbCheckService;
import com.wxlsoft.config.model.EsbCollectors;
import com.wxlsoft.config.model.EsbProtocol;
import com.wxlsoft.config.model.EsbPublishSelect;
import com.wxlsoft.config.model.EsbResult;
import com.wxlsoft.config.model.EsbServiceFlow;
import com.wxlsoft.config.model.EsbServiceidentify;
import com.wxlsoft.config.model.GenralConfigProperties;
import com.wxlsoft.config.model.QueryTokenBody;
import com.wxlsoft.ui.ShowErrorMessage;
import com.wxlsoft.utils.JsonUtils;
import com.wxlsoft.utils.LogUtils;
import com.wxlsoft.utils.Maps;

import cn.hutool.core.date.LocalDateTimeUtil;
import okhttp3.MediaType;
import okhttp3.MultipartBody;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.Request.Builder;
import okhttp3.RequestBody;
import okhttp3.Response;
import okhttp3.logging.HttpLoggingInterceptor;
import okhttp3.logging.HttpLoggingInterceptor.Level;

public class EsbPlatform extends AbstractDeployHandler {

	private static final OkHttpClient httpClient;
	static {
		HttpLoggingInterceptor logger = new HttpLoggingInterceptor();
		logger.setLevel(Level.BODY);
		httpClient = new OkHttpClient().newBuilder()
				.readTimeout(30, TimeUnit.SECONDS)
				.followRedirects(false)
				.addInterceptor(logger)
				.build();
	}
	
	/**
	 * 配置参数
	 */
	private GenralConfigProperties configProperties;
	
	/**
	 * 服务端配置结果
	 */
	private EsbCollectors comboVlues = new EsbCollectors();
	
	/**
	 * 选中参数
	 */
	private EsbPublishSelect selected;
	
	/**
	 * @param configProperties
	 */
	public EsbPlatform(GenralConfigProperties configProperties) {
		this.configProperties = configProperties;
	}

	/**
	 * 向控制台发送请求
	 * @param url
	 * @param body
	 * @return
	 */
	public EsbResult request(String url, Object body, Map<String, String> headers) throws Exception {
		Builder builder = new Request.Builder().url(url);
		if (!headers.isEmpty()) {
			for (Entry<String, String> header : headers.entrySet()) {
				builder.addHeader(header.getKey(), header.getValue());
			}
		}
		if (Objects.nonNull(body)) {
			builder.post(body instanceof RequestBody ? (RequestBody) body :  RequestBody.create(JsonUtils.toJson(body).getBytes()));
		}
		Response response = httpClient.newCall(builder.build()).execute();
		String message = new String(response.body().bytes());
		if (!response.isSuccessful()) {
			throw new RuntimeException(message);
		}
		EsbResult result = JsonUtils.fromJson(message, EsbResult.class);
		if (!result.isSuccess()) {
			throw new RuntimeException(message);
		}
		return result;
	}
	
	/**
	 * 模拟登陆, 返回 token
	 * @throws Exception 
	 */
	private void mocklogin() throws Exception {
		String url = String.format("%s/api/blade-auth/token", configProperties.getPortalUrl());
		QueryTokenBody body = new QueryTokenBody(configProperties.getUsername(), configProperties.getPassword());
		EsbResult result = request(url, body, Maps.jsonHeaders());
		configProperties.setToken(result.token());
	}
	
	/**
	 * 查询应用列表
	 * @return
	 */
	public List<EsbAppInfo> queryAppIds() throws Exception {
		List<EsbAppInfo> appIds = comboVlues.getAppIds();
		if (appIds.isEmpty()) {
			LogUtils.info(String.format("Dcits >>>>>> 查询应用列表"));
			String url = String.format("%s/api/architecture/gatewayinfo/list?current=1&size=100", configProperties.getPortalUrl());
			EsbResult result = request(url, null, Maps.jsonHeaders(token()));
			
			LogUtils.info(String.format("Dcits >>>>>> 查询应用列表简称"));
			String simurl = String.format("%s/api/architecture/systeminfo/getSystemAll", configProperties.getPortalUrl());
			EsbResult simresult = request(simurl, null, Maps.jsonHeaders(token()));
			comboVlues.setAppIds(EsbAppInfo.parse(result.getData(), simresult.getData()));
		}
		return comboVlues.getAppIds();
	}
	
	private String token() throws Exception {
		String token = configProperties.getToken();
		if (Objects.isNull(token)) {
			mocklogin();
		}
		return configProperties.getToken();
	}

	/**
	 * 查询协议信息
	 * @return 
	 */
	public List<EsbProtocol> queryProtocols(String appid) throws Exception {
		List<EsbProtocol> protocols = comboVlues.getProtocols();
		if (protocols.isEmpty()) {
			LogUtils.info(String.format("Dcits >>>>>> 查询协议列表", "appId"));
			String url = String.format("%s/api/architecture/protocolinfo/list?current=1&size=100&serviceName=%s", configProperties.getPortalUrl(), appid);
			EsbResult result = request(url, null, Maps.jsonHeaders(token()));
			comboVlues.setProtocols(EsbProtocol.parse(result.getData()));
		}
		return comboVlues.getProtocols();
	}
	
	/**
	 * 查询服务识别列表，暂时不用
	 * @return 
	 */
	public List<EsbServiceidentify> queryServiceidentify(String appid) throws Exception {
		List<EsbServiceidentify> services = comboVlues.getServiceidentify();
		if (services.isEmpty()) {
			LogUtils.info(String.format("Dcits >>>>>> 查询协议列表", "appId"));
			String url = String.format("%s/api/architecture/serviceidentify/list?current=1&size=10&remark=%s", configProperties.getPortalUrl(), appid);
			EsbResult result = request(url, null, Maps.jsonHeaders(token()));
			comboVlues.setServiceidentify(EsbServiceidentify.parse(result.getData()));
		}
		return comboVlues.getServiceidentify();
	}
	
	/**
	 * 查询服务流程
	 * @return 
	 */
	public List<EsbServiceFlow> queryServiceFlow(String appid) throws Exception {
		List<EsbServiceFlow> serviceFlow = comboVlues.getServiceFlow();
		if (serviceFlow.isEmpty()) {
			LogUtils.info(String.format("Dcits >>>>>> 查询流程列表", "appId"));
			String url = String.format("%s/api/architecture/serviceinfo/getService?appId=%s", configProperties.getPortalUrl(), appid);
			EsbResult result = request(url, "", Maps.jsonHeaders(token()));
			comboVlues.setServiceFlow(EsbServiceFlow.parse(result.getData()));
		}
		return comboVlues.getServiceFlow();
	}
	
	public void setSelected(EsbPublishSelect selected) {
		this.selected = selected;
	}
	
	/**
	 * 发布配置信息
	 * @throws Exception 
	 */
	public void publish() throws Exception {
		// 写 key-value
		final Collection<ConfigFileResource> resources = resources();
		if (resources.isEmpty()) {
			LogUtils.info(String.format("Dcits >>>>>> No Selected Resource, Program Will Be Exit"));
			return;
		}
		
		for (ConfigFileResource configfile : resources) {
			if (!configfile.conform()) {
				ShowErrorMessage.show(String.format("拆组包文件不全：%s (需要包含服务定义，客户/服务端拆包、组包)", configfile.getService()));
				return;
			}
			itemCreateOrUpdate(configfile);
		}
	}

	// 1、检查文件是否已存在
	private EsbCheckService checkService(ConfigFileResource configfile) throws Exception {
		LogUtils.info(String.format("正在进行服务注册检查：%s", configfile.getTrancode()));
		String curl = String.format("%s/api/architecture/servicedefinition/checkList?versionNo=%s&micserviceId=%s&serviceId=%s", 
				configProperties.getPortalUrl(), configfile.getVersion(), selected.getAppInfo().getServiceName(), configfile.getTrancode());
		EsbResult result = request(curl, null, Maps.jsonHeaders(token()));
		EsbCheckService checkService = EsbCheckService.parse(result.getData());
		return checkService;
	}
	
	/**
	 * 创建或更新服务
	 * @param name
	 * @param name2
	 * @param readUtf8Str
	 */
	private void itemCreateOrUpdate(ConfigFileResource configfile) throws Exception {
		// 1、检查文件是否已存在
		EsbCheckService checkService = checkService(configfile);
		// 2、创建或更新服务注册
		registerOrUpdateService(configfile, checkService);
		// 3、检查服务识别是否已注册
		EsbCheckService checkServiceDefined = checkServiceDefined(configfile);
		// 4、添加到服务识别
		createServiceDefined(configfile, checkServiceDefined);
	}

	// 创建或更新服务注册
	@SuppressWarnings("deprecation")
	private EsbResult registerOrUpdateService(ConfigFileResource configfile, EsbCheckService checkService) throws Exception {
		LogUtils.info(String.format("正在进行服务注册或更新：%s", configfile.getTrancode()));
		
		String url = String.format("%s/api/architecture/service/%s", configProperties.getPortalUrl(), checkService.getSaveType());
		MediaType mediaType = MediaType.parse("text/xml");
		MultipartBody.Builder builder = new MultipartBody.Builder().setType(MultipartBody.FORM)	// MediaType.parse("multipart/form-data; boundary=----WebKitFormBoundaryi7KKF9erOuhYYoYk")
				.addFormDataPart("basicInfo", "")
				.addFormDataPart("serviceId", configfile.getTrancode())
        		.addFormDataPart("versionNo", configfile.getVersion())
	        	.addFormDataPart("serviceName", configfile.getTrancode())
	        	.addFormDataPart("interfaceUrl", "/")
	        	.addFormDataPart("micserviceId", selected.getAppInfo().getServiceName())
	        	.addFormDataPart("systemId", selected.getAppInfo().getSystemId())
	        	.addFormDataPart("groupService", "N")
	        	.addFormDataPart("sgVersion", "V1.1.1")
	        	.addFormDataPart("packageType", selected.getInProtocal().getPackageType())
	        	.addFormDataPart("status", checkService.getStatus())
	        	.addFormDataPart("isCR", "false")
	        	.addFormDataPart("isgns", "false")
	        	.addFormDataPart("servicePriority", "3")
	        	.addFormDataPart("serviceDesc", "自动化方式注册")
	        	.addFormDataPart("adapterflowId", selected.getFlow())
	        	.addFormDataPart("protocolIdOutTitle", "")
	        	.addFormDataPart("protocolIdOut", selected.getOutProtocal())
	        	.addFormDataPart("p", "")
	        	.addFormDataPart("cpStatus", "0")
	        	.addFormDataPart("lastTime", LocalDateTimeUtil.format(LocalDateTime.now(), "yyyy-MM-dd hh:mm:ss"))	// update接口
	        	.addFormDataPart("type", "provider")		// update接口
//	        	.addFormDataPart("sid", "${sid}")			// update接口
	        	.addFormDataPart("onVersionNo", configfile.getVersion())	// update接口
	        	.addFormDataPart("model", selected.getInProtocal().getPackageType())	// update接口
	        	.addFormDataPart("id", checkService.getId())		// update接口
	        	.addFormDataPart("optUser", selected.getAppInfo().getServiceName())	// update接口
	        	.addFormDataPart("serviceSystem", selected.getAppInfo().getSystemId())		// update接口
	        	.addFormDataPart("target", selected.getAppInfo().getServiceName())		// update接口
	        	.addFormDataPart("optDate", LocalDateTimeUtil.format(LocalDateTime.now(), "yyyy-MM-dd hh:mm:ss"))	// update接口
	        	.addFormDataPart("$index", "0")		// update接口
	        	.addFormDataPart("$groupService", "否")
	        	.addFormDataPart("$packageType", selected.getInProtocal().getPackageType())
	        	.addFormDataPart("$systemId", selected.getAppInfo().getSystemText())		// update接口
	        	.addFormDataPart("$isCR", "否")
	        	.addFormDataPart("$isgns", "否")
	        	.addFormDataPart("$cpStatus", checkService.getCpstatus())
	        	.addFormDataPart("wsFunction", "")
	        	.addFormDataPart("appId", selected.getAppInfo().getServiceName())
	        	.addFormDataPart("fileName", configfile.getService().getName())	// servicename
	        	.addFormDataPart("serviceDefFile", configfile.getService().getName(), RequestBody.create(mediaType, configfile.getService().getFile()))	// serviceDefFile binary
	        	.addFormDataPart("filePath", configfile.getProdIn().getName())
        		.addFormDataPart("file1", configfile.getProdIn().getName(), RequestBody.create(mediaType, configfile.getProdIn().getFile()))
        		.addFormDataPart("filePath2", configfile.getProdOut().getName())
        		.addFormDataPart("file2", configfile.getProdOut().getName(), RequestBody.create(mediaType, configfile.getProdOut().getFile()));
		return request(url, builder.build(), Maps.multipartHeaders(configProperties.getToken()));
	}

	// 3、检查服务识别是否已注册
	private EsbCheckService checkServiceDefined(ConfigFileResource configfile) throws Exception {
		LogUtils.info(String.format("正在进行服务识别检查：%s", configfile.getTrancode()));
		String curl = String.format("%s/api/architecture/serviceidentifyinvokeWithCpConfig/list?invokeId=%s&current=1&size=10&identifyKey=%s", 
				configProperties.getPortalUrl(), selected.getServiceidentify().getInvokeId(), configfile.getTrancode());
		EsbResult result = request(curl, null, Maps.jsonHeaders(token()));
		return EsbCheckService.definedParse(result.getData());
	}
	
	// 4、添加到服务识别
	@SuppressWarnings("deprecation")
	private EsbResult createServiceDefined(ConfigFileResource configfile, EsbCheckService checkService) throws Exception {
		LogUtils.info(String.format("添加或更新服务识别规则：%s", configfile.getTrancode()));
		String curl = String.format("%s/api/architecture/serviceidentifyinvokeWithCpConfig/%s", configProperties.getPortalUrl(), checkService.getSaveType());
		MediaType mediaType = MediaType.parse("text/xml");
		
		if (checkService.getSaveType().equals("update")) {
			MultipartBody.Builder builder = new MultipartBody.Builder().setType(MultipartBody.FORM)	// MediaType.parse("multipart/form-data; boundary=----WebKitFormBoundaryi7KKF9erOuhYYoYk")
					.addFormDataPart("eid", checkService.getId())
	        		.addFormDataPart("identifyKey", configfile.getTrancode())
		        	.addFormDataPart("identifyValue", configfile.getTrancode())
		        	.addFormDataPart("identifyVersion", configfile.getVersion())
		        	.addFormDataPart("file1", configfile.getClientIn().getName(), RequestBody.create(mediaType, configfile.getClientIn().getFile()))
	        		.addFormDataPart("file2", configfile.getClientOut().getName(), RequestBody.create(mediaType, configfile.getClientOut().getFile()));
			return request(curl, builder.build(), Maps.multipartHeaders(configProperties.getToken()));
		}
		
		MultipartBody.Builder builder = new MultipartBody.Builder().setType(MultipartBody.FORM)	// MediaType.parse("multipart/form-data; boundary=----WebKitFormBoundaryi7KKF9erOuhYYoYk")
				.addFormDataPart("basicInfo", "")
				.addFormDataPart("invokeId", selected.getServiceidentify().getInvokeId())
        		.addFormDataPart("identifyKey", configfile.getTrancode())
	        	.addFormDataPart("type", selected.getServiceidentify().getIdentifyType())
	        	.addFormDataPart("identifyValue", configfile.getTrancode())
	        	.addFormDataPart("identifyVersion", configfile.getVersion())
	        	.addFormDataPart("c", "")
	        	.addFormDataPart("$identifyValue", String.format("%s(%s)", configfile.getTrancode(), configfile.getTrancode()))
	        	.addFormDataPart("$identifyVersion", configfile.getVersion())
	        	.addFormDataPart("identifyCharacter", "UTF-8")
	        	.addFormDataPart("identifyExpression", selected.getServiceidentify().getIdentifyExpression())
	        	.addFormDataPart("identifyModel", selected.getServiceidentify().getIdentifyModel())
	        	.addFormDataPart("optDate", LocalDateTimeUtil.format(LocalDateTime.now(), "yyyy-MM-dd hh:mm:ss"))
	        	.addFormDataPart("optUser", configProperties.getUsername())
	        	.addFormDataPart("protocolName", selected.getInProtocal().getProtocolName())
	        	.addFormDataPart("remark", selected.getAppInfo().getServiceName())
	        	.addFormDataPart("identifyState", "2")
	        	.addFormDataPart("identifyType", selected.getServiceidentify().getIdentifyType())
	        	.addFormDataPart("$index", "0")
	        	.addFormDataPart("$protocolName", selected.getInProtocal().getProtocolName())
	        	.addFormDataPart("$identifyType", selected.getServiceidentify().getIdentifyType())	// update接口
	        	.addFormDataPart("$identifyModel", selected.getInProtocal().getPackageType())		// update接口
	        	.addFormDataPart("filePath", configfile.getClientIn().getName())
        		.addFormDataPart("file1", configfile.getClientIn().getName(), RequestBody.create(mediaType, configfile.getClientIn().getFile()))
        		.addFormDataPart("filePath2", configfile.getClientOut().getName())
        		.addFormDataPart("file2", configfile.getClientOut().getName(), RequestBody.create(mediaType, configfile.getClientOut().getFile()));
		EsbResult response = request(curl, builder.build(), Maps.multipartHeaders(configProperties.getToken()));
		return response;
	}

}
